import {type WebSocketMessage, WebSocketStatus} from "@/type/utils/websocket";

type EventListener = (...args: any[]) => void;

export class WebSocketClient {
    private ws: WebSocket | null = null;
    private reconnectAttempts = 0;
    private heartbeatTimer: ReturnType<typeof setTimeout> | null = null;
    private pongTimer: ReturnType<typeof setTimeout> | null = null;
    private isManualClose = false;
    private eventListeners = new Map<string, EventListener[]>();
    private sendBuffer: Array<{ payload: WebSocketMessage; attempts: number }> = [];


    private autoReconnect = true
    private reconnectInterval = 3000
    private maxReconnectAttempts = 5
    private heartbeatInterval = 30000
    private heartbeatTimeout = 5000
    private reconnectBackoffFactor = 1.5
    private sendBufferMaxSize = 100
    private resendOnReconnect = true

    private webSocketUrl: any;

    constructor(webSocketUrl: string) {
        this.webSocketUrl = webSocketUrl;
    }

    public connect(): void {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) return;
        try {
            this.ws = new WebSocket(this.webSocketUrl);
            this.ws.onopen = this.handleOpen;
            this.ws.onmessage = this.handleMessage;
            this.ws.onerror = this.handleError;
            this.ws.onclose = this.handleClose;
        } catch (err) {
            console.error("[WebSocket] 创建连接失败:", err);
            this.handleReconnect();
        }
    }

    // 公共 API
    public send(payload: WebSocketMessage): boolean {
        const msg: WebSocketMessage = {
            ...payload,
            timestamp: payload.timestamp ?? Date.now(),
        };
        if (this.isConnected()) {
            try {
                this.ws!.send(JSON.stringify(msg));
                return true;
            } catch (err) {
                console.error("[WebSocket] 发送失败:", err);
                if (this.resendOnReconnect) this.enqueueBuffer(msg);
                return false;
            }
        } else {
            if (this.resendOnReconnect) this.enqueueBuffer(msg);
            return false;
        }
    }

    public close(code = 1000, reason?: string): void {
        this.isManualClose = true;
        this.stopHeartbeat();
        this.ws?.close(code, reason);
    }

    public reconnectNow(): void {
        this.isManualClose = false;
        this.reconnectAttempts = 0;
        this.close(1000, "manual reconnect");
        // 立即发起连接
        setTimeout(() => this.connect(), 0);
    }

    public isConnected(): boolean {
        return this.ws?.readyState === WebSocket.OPEN;
    }

    public getReadyState(): WebSocketStatus {
        return this.ws?.readyState ?? WebSocketStatus.CLOSED;
    }

    public on(event: "open", listener: () => void): void;

    public on(event: "message", listener: (msg: WebSocketMessage) => void): void;

    public on(event: "error", listener: (evt: Event) => void): void;

    public on(event: "close", listener: (evt: CloseEvent) => void): void;

    public on(event: string, listener: EventListener): void {
        if (!this.eventListeners.has(event)) {
            this.eventListeners.set(event, []);
        }
        this.eventListeners.get(event)!.push(listener);
    }

    public off(event: string, listener: EventListener): void {
        const listeners = this.eventListeners.get(event);
        if (!listeners) return;
        const idx = listeners.indexOf(listener);
        if (idx !== -1) listeners.splice(idx, 1);
    }

    private handleOpen = (): void => {
        this.reconnectAttempts = 0;
        this.emit("open");
        this.startHeartbeat();
        this.flushSendBuffer();
    };

    private handleMessage = (evt: MessageEvent): void => {
        let msg: any;
        try {
            msg = evt.data;
        } catch {
            this.emit("error", new Error("消息解析失败"));
            return;
        }

        // 心跳响应
        if (typeof msg == 'string') {
            let data = JSON.parse(msg);
            if (data.type === "pong") {
                this.handlePong();
                return;
            }
        }

        this.emit("message", msg);
    };

    private handleError = (evt: Event): void => {
        this.emit("error", evt);
    };

    private handleClose = (evt: CloseEvent): void => {
        this.stopHeartbeat();
        this.emit("close", evt);

        if (!this.isManualClose && this.autoReconnect) {
            this.handleReconnect();
        }
    };

    private handleReconnect = (): void => {
        if (
            this.reconnectAttempts >= this.maxReconnectAttempts ||
            this.isManualClose
        ) {
            console.warn("[WebSocket] 达到最大重连次数或已手动关闭，停止重连");
            return;
        }
        const base = this.reconnectInterval;
        const backoff = base * Math.pow(this.reconnectBackoffFactor, this.reconnectAttempts);
        const delay = Math.min(backoff, 30000); // 上限 30s
        this.reconnectAttempts += 1;
        console.log(`[WebSocket] ${delay}ms 后进行第 ${this.reconnectAttempts} 次重连...`);
        setTimeout(() => this.connect(), delay);
    };

    private startHeartbeat(): void {
        if (!this.heartbeatInterval) return;
        this.stopHeartbeat();
        this.heartbeatTimer = setInterval(() => {
            if (this.isConnected()) {
                this.ws!.send(JSON.stringify({type: "ping", timestamp: Date.now()}));
                // 启动 pong 超时计时
                this.pongTimer = setTimeout(() => {
                    console.warn("[WebSocket] pong 超时，主动关闭连接以触发重连");
                    this.ws?.close(1006, "pong timeout");
                }, this.heartbeatTimeout);
            }
        }, this.heartbeatInterval);
    }

    private stopHeartbeat(): void {
        if (this.heartbeatTimer) {
            clearInterval(this.heartbeatTimer);
            this.heartbeatTimer = null;
        }
        if (this.pongTimer) {
            clearTimeout(this.pongTimer);
            this.pongTimer = null;
        }
    }

    private handlePong(): void {
        if (this.pongTimer) {
            clearTimeout(this.pongTimer);
            this.pongTimer = null;
        }
    }

    private flushSendBuffer(): void {
        if (!this.resendOnReconnect) {
            this.sendBuffer = [];
            return;
        }
        while (this.sendBuffer.length > 0 && this.isConnected()) {
            const item = this.sendBuffer.shift()!;
            this.ws!.send(JSON.stringify(item.payload));
        }
    }

    private enqueueBuffer(payload: WebSocketMessage): void {
        if (this.sendBuffer.length >= this.sendBufferMaxSize) {
            console.warn("[WebSocket] 发送队列已满，丢弃消息", payload);
            return;
        }
        this.sendBuffer.push({payload, attempts: 0});
    }

    private emit(event: string, ...args: any[]): void {
        const listeners = this.eventListeners.get(event);
        if (listeners) {
            listeners.slice().forEach(fn => {
                try {
                    fn(...args);
                } catch (err) {
                    console.error(`[WebSocket] 事件回调执行出错 (${event}):`, err);
                }
            });
        }
    }
}